library(readr)
library(glue)
library(tidyverse) # to load tidyr, dplyr, ggplot2,...
# Text Mining ----------------------------------------
library(lsa) # stopwords in german
library(SnowballC) # word trunkating
library(tidytext) # text mining
# Visualization --------------------------------------
library(igraph) # networks viz
#library(wordcloud) # wordcloud viz
library(widyr)


data_categorized <- read_delim("data_categorized.csv", 
    ";", escape_double = FALSE, trim_ws = TRUE) %>% rowid_to_column()
#View(data_categorized)

questions_for_analysis <- read_csv("questions_for_analysis.csv")
data(stopwords_de)

stopwords_de <- data_frame(token = stopwords_de)
#' Tokenizes the text in a data frame removing numbers, signs and stopwords 
#'
#' @param df Input dataframe. 
#' @param curr_q dataframe with correspondence of current question numbers across the surveys.
#' @param custom_stopwords custom words to remove from dataframe.
#' @return Input dataframe with new colum "token" with 1 row per word.
tokenize_and_clean <- function(df = data_categorized, curr_q, custom_stopwords = NULL){
  data_categorized %>% 
    filter((Jahr == 2014 & Frage %in% curr_q$year_2014) | (Jahr == 2015 & Frage %in% curr_q$year_2015) | (Jahr == 2017 & Frage %in% curr_q$year_2017)) %>% # filter current question(s)
    unnest_tokens(token, Text) %>% # tokenize
    filter(str_detect(token, "[a-z]")) %>% # keep only words, remove numbers and signs 
    anti_join(stopwords_de, by = "token") %>% # remove stopwords
    filter(!(token %in% c("sog", "esf", "isea", "ja", "damit", "nein", "denn", "er", "projekt", custom_stopwords)) ) # remove custom stopwords
}
#' Generates Barplot of most common words.
#'
#' @param df Input (tokenized) dataframe. 
#' @param curr_q dataframe with correspondence of current question numbers across the surveys and labels for printing titles.
#' @return Barplot
count_words_ungrouped <- function(df, curr_q){
  clean.df %>%
    count(token, sort = TRUE) %>%
    filter(n > 1) %>% 
    top_n(n = 9, wt= n) %>% 
    mutate(token = reorder(token, n)) %>%
    ggplot(aes(token, n)) +
    theme_light() +
    geom_col(show.legend = FALSE) +
    coord_flip() + ggtitle(unique(curr_q$Label), subtitle = "Top 10 words")
}
#' Generates Barplot of most common words BY GENDER.
#'
#' @param df Input (tokenized) dataframe. 
#' @param curr_q dataframe with correspondence of current question numbers across the surveys and labels for printing titles.
#' @return Barplot
count_words_group_gender <- function(df, curr_q){
  df %>%
  filter(Geschlecht %in% c("männlich", "weiblich")) %>% 
  group_by(Geschlecht) %>%
  count(token, sort = TRUE) %>%
  filter(n > 1) %>% 
  top_n(n = 9, wt= n) %>% 
  ungroup() %>%
  mutate(token = reorder(token, n)) %>%
  ggplot(aes(token, n, fill = Geschlecht)) +
  theme_light() +
  geom_col(show.legend = FALSE) +
  facet_grid( ~ Geschlecht) +
  coord_flip() + ggtitle(unique(curr_q$Label), subtitle = "Top 10 words by Gender")
}
#' Generates Barplot of most common words BY YEAR
#'
#' @param df Input (tokenized) dataframe. 
#' @param curr_q dataframe with correspondence of current question numbers across the surveys and labels for printing titles.
#' @return Barplot
count_words_group_year <- function(df, curr_q){
  df %>%
  group_by(Jahr) %>%
  count(token, sort = TRUE) %>%
  filter(n > 1) %>% 
  top_n(n = 9, wt= n) %>% 
  ungroup() %>%
  mutate(token = reorder(token, n)) %>%
  ggplot(aes(token, n, fill = Jahr)) +
  theme_light() +
  geom_col(show.legend = FALSE) +
  facet_grid( ~ Jahr) +
  coord_flip() + ggtitle(unique(curr_q$Label), subtitle = "Top 10 words over year")
}
#' Generates Network words used over some threshold with following specs:
#' Node size = how much the words was used.
#' Edge size = how many times these words have been used together.
#'
#' @param df Input (tokenized) dataframe. 
#' @param threshold threshold fo display words with count > threshold
#' @param curr_q dataframe with correspondence of current question numbers across the surveys and labels for printing titles.
#' @param edge_w factor to scale edge width
#' @param vertex_s factor to scale vertex size
#' @return Network
network_generate <- function(df = clean.df, threshold, curr_q, edge_w = 3, vertex_s = 1/2, vertex_label_dist = 0.6, vertex_label_cex = 1.8){
  # We save the network plot as a pdf file.
  #pdf(paste("exported_nw", unique(curr_q$Label), paste(unique(df$Jahr), collapse = "_"), ".pdf"))
  # Compute pairwise word count. 
  pair_wise_count_df <- df %>% 
  select(token, rowid) %>%
  pairwise_count(item = token, feature = rowid) %>% 
  filter(item1 < item2) %>% arrange(item1, item2) %>% 
  arrange(- n)
  # Define network object. 
  network <-  pair_wise_count_df %>%
    filter(n > threshold) %>% # apply threshold
    graph_from_data_frame(directed = FALSE)
  
  # Store the degree.
  V(network)$degree <- degree(graph = network)
  
  # Compute the weight shares.
  E(network)$width <- E(network)$n/max(E(network)$n)
   
  # Plot the network.
  plot(network, 
       vertex.color = 'lightblue',
       # Scale node size by degree.
       vertex.size = vertex_s*V(network)$degree,
       vertex.label.color = 'black', 
       vertex.label.cex = vertex_label_cex, 
       vertex.label.dist = vertex_label_dist,
       edge.color = 'gray', 
       # Set edge width proportional to the weight relative value.
       edge.width = edge_w*E(network)$width,
       main =  paste(unique(curr_q$Label), "for suveys:", paste(unique(df$Jahr), collapse = ",")), 
       sub = glue('Weight Threshold: {threshold}'), 
       alpha = 50)
  #dev.off()
}

SOG Improvements

curr_q_num <- 1
curr_q <- questions_for_analysis %>% filter(ID == curr_q_num)
clean.df <- tokenize_and_clean(data_categorized, curr_q, custom_stopwords = c("gibt", "institution", "projekt", "stipendienprogramm", "programm", "stipendiaten"))
count_words_ungrouped(clean.df, curr_q)

count_words_group_gender(clean.df, curr_q)

count_words_group_year(clean.df, curr_q)

# Set a threshold for visualization.
threshold <- 1
network_generate(df = clean.df, threshold, curr_q, edge_w = 3, vertex_s = 1/2, vertex_label_dist = 0.6, vertex_label_cex = 1.8)

# Set a threshold for visualization.
#threshold <- 1
#network_generate(df = filter(clean.df, Jahr == 2014), threshold, curr_q)
# Set a threshold for visualization.
#threshold <- 1
#network_generate(df = filter(clean.df, Jahr == 2015), threshold, curr_q)
# Set a threshold for visualization.
#threshold <- 0
#network_generate(df = filter(clean.df, Jahr == 2017), threshold, curr_q)

Difficulties in the past academic year

curr_q_num <- 2
curr_q <- questions_for_analysis %>% filter(ID == curr_q_num)
clean.df <- tokenize_and_clean(data_categorized, curr_q, custom_stopwords = c("schwierigkeiten"))
count_words_ungrouped(clean.df, curr_q)

count_words_group_gender(clean.df, curr_q)

count_words_group_year(clean.df, curr_q)

# Set a threshold for visualization.
threshold <- 1
network_generate(df = clean.df, threshold, curr_q, edge_w = 3, vertex_s = 1/2, vertex_label_dist = 0.6, vertex_label_cex = 1.8)

# Set a threshold for visualization.
#threshold <- 1
#network_generate(df = filter(clean.df, Jahr == 2014), threshold, curr_q)
# Set a threshold for visualization.
#threshold <- 1
#network_generate(df = filter(clean.df, Jahr == 2015), threshold, curr_q)
# Set a threshold for visualization.
#threshold <- 0
#network_generate(df = filter(clean.df, Jahr == 2017), threshold, curr_q)

Possible risks of project implementation

curr_q_num <- 3
curr_q <- questions_for_analysis %>% filter(ID == curr_q_num)
clean.df <- tokenize_and_clean(data_categorized, curr_q)
count_words_ungrouped(clean.df, curr_q)

count_words_group_gender(clean.df, curr_q)

count_words_group_year(clean.df, curr_q)

# Set a threshold for visualization.
threshold <- 1
network_generate(df = clean.df, threshold, curr_q)

# Set a threshold for visualization.
#threshold <- 1
#network_generate(df = filter(clean.df, Jahr == 2014), threshold, curr_q)
# Set a threshold for visualization.
#threshold <- 1
#network_generate(df = filter(clean.df, Jahr == 2015), threshold, curr_q)
# Set a threshold for visualization.
#threshold <- 0
#network_generate(df = filter(clean.df, Jahr == 2017), threshold, curr_q)

Changes in Life Situation

curr_q_num <- 5
curr_q <- questions_for_analysis %>% filter(ID == curr_q_num)
clean.df <- tokenize_and_clean(data_categorized, curr_q, custom_stopwords = c("situation", "verändert", "veränderung", "jahr", "geändert", "gibt"))
count_words_ungrouped(clean.df, curr_q)

count_words_group_gender(clean.df, curr_q)

count_words_group_year(clean.df, curr_q)

# Set a threshold for visualization.
threshold <- 1
network_generate(df = clean.df, threshold, curr_q, vertex_s = 1)

# Set a threshold for visualization.
#threshold <- 1
#network_generate(df = filter(clean.df, Jahr == 2014), threshold, curr_q)
# Set a threshold for visualization.
#threshold <- 1
#network_generate(df = filter(clean.df, Jahr == 2015), threshold, curr_q)
# Set a threshold for visualization.
#threshold <- 0
#network_generate(df = filter(clean.df, Jahr == 2017), threshold, curr_q)

Wishes for next year

curr_q_num <- 6
curr_q <- questions_for_analysis %>% filter(ID == curr_q_num)
clean.df <- tokenize_and_clean(data_categorized, curr_q, custom_stopwords = c("jahr", "möchte", "nächtes", "nächstes", "wünsche", "wunsch", "hofft"))
count_words_ungrouped(clean.df, curr_q)

count_words_group_gender(clean.df, curr_q)

count_words_group_year(clean.df, curr_q)

# Set a threshold for visualization.
threshold <- 1
network_generate(df = clean.df, threshold, curr_q)

# Set a threshold for visualization.
#threshold <- 1
#network_generate(df = filter(clean.df, Jahr == 2014), threshold, curr_q)
# Set a threshold for visualization.
#threshold <- 1
#network_generate(df = filter(clean.df, Jahr == 2015), threshold, curr_q)
# Set a threshold for visualization.
#threshold <- 0
#network_generate(df = filter(clean.df, Jahr == 2017), threshold, curr_q)
LS0tCnRpdGxlOiAiRFNTRyAyMDE5IC0gU09HIFRleHQgQW5hbHlzaXMiCmF1dGhvcjogIkV2YSBNYXJ0aW5leiIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCi0tLQoKYGBge3J9CmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZ2x1ZSkKbGlicmFyeSh0aWR5dmVyc2UpICMgdG8gbG9hZCB0aWR5ciwgZHBseXIsIGdncGxvdDIsLi4uCiMgVGV4dCBNaW5pbmcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpsaWJyYXJ5KGxzYSkgIyBzdG9wd29yZHMgaW4gZ2VybWFuCmxpYnJhcnkoU25vd2JhbGxDKSAjIHdvcmQgdHJ1bmthdGluZwpsaWJyYXJ5KHRpZHl0ZXh0KSAjIHRleHQgbWluaW5nCiMgVmlzdWFsaXphdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpsaWJyYXJ5KGlncmFwaCkgIyBuZXR3b3JrcyB2aXoKI2xpYnJhcnkod29yZGNsb3VkKSAjIHdvcmRjbG91ZCB2aXoKbGlicmFyeSh3aWR5cikKCgpkYXRhX2NhdGVnb3JpemVkIDwtIHJlYWRfZGVsaW0oImRhdGFfY2F0ZWdvcml6ZWQuY3N2IiwgCiAgICAiOyIsIGVzY2FwZV9kb3VibGUgPSBGQUxTRSwgdHJpbV93cyA9IFRSVUUpICU+JSByb3dpZF90b19jb2x1bW4oKQojVmlldyhkYXRhX2NhdGVnb3JpemVkKQoKcXVlc3Rpb25zX2Zvcl9hbmFseXNpcyA8LSByZWFkX2NzdigicXVlc3Rpb25zX2Zvcl9hbmFseXNpcy5jc3YiKQpgYGAKCmBgYHtyfQpkYXRhKHN0b3B3b3Jkc19kZSkKCnN0b3B3b3Jkc19kZSA8LSBkYXRhX2ZyYW1lKHRva2VuID0gc3RvcHdvcmRzX2RlKQpgYGAKCmBgYHtyfQojJyBUb2tlbml6ZXMgdGhlIHRleHQgaW4gYSBkYXRhIGZyYW1lIHJlbW92aW5nIG51bWJlcnMsIHNpZ25zIGFuZCBzdG9wd29yZHMgCiMnCiMnIEBwYXJhbSBkZiBJbnB1dCBkYXRhZnJhbWUuIAojJyBAcGFyYW0gY3Vycl9xIGRhdGFmcmFtZSB3aXRoIGNvcnJlc3BvbmRlbmNlIG9mIGN1cnJlbnQgcXVlc3Rpb24gbnVtYmVycyBhY3Jvc3MgdGhlIHN1cnZleXMuCiMnIEBwYXJhbSBjdXN0b21fc3RvcHdvcmRzIGN1c3RvbSB3b3JkcyB0byByZW1vdmUgZnJvbSBkYXRhZnJhbWUuCiMnIEByZXR1cm4gSW5wdXQgZGF0YWZyYW1lIHdpdGggbmV3IGNvbHVtICJ0b2tlbiIgd2l0aCAxIHJvdyBwZXIgd29yZC4KdG9rZW5pemVfYW5kX2NsZWFuIDwtIGZ1bmN0aW9uKGRmID0gZGF0YV9jYXRlZ29yaXplZCwgY3Vycl9xLCBjdXN0b21fc3RvcHdvcmRzID0gTlVMTCl7CiAgZGF0YV9jYXRlZ29yaXplZCAlPiUgCiAgICBmaWx0ZXIoKEphaHIgPT0gMjAxNCAmIEZyYWdlICVpbiUgY3Vycl9xJHllYXJfMjAxNCkgfCAoSmFociA9PSAyMDE1ICYgRnJhZ2UgJWluJSBjdXJyX3EkeWVhcl8yMDE1KSB8IChKYWhyID09IDIwMTcgJiBGcmFnZSAlaW4lIGN1cnJfcSR5ZWFyXzIwMTcpKSAlPiUgIyBmaWx0ZXIgY3VycmVudCBxdWVzdGlvbihzKQogICAgdW5uZXN0X3Rva2Vucyh0b2tlbiwgVGV4dCkgJT4lICMgdG9rZW5pemUKICAgIGZpbHRlcihzdHJfZGV0ZWN0KHRva2VuLCAiW2Etel0iKSkgJT4lICMga2VlcCBvbmx5IHdvcmRzLCByZW1vdmUgbnVtYmVycyBhbmQgc2lnbnMgCiAgICBhbnRpX2pvaW4oc3RvcHdvcmRzX2RlLCBieSA9ICJ0b2tlbiIpICU+JSAjIHJlbW92ZSBzdG9wd29yZHMKICAgIGZpbHRlcighKHRva2VuICVpbiUgYygic29nIiwgImVzZiIsICJpc2VhIiwgImphIiwgImRhbWl0IiwgIm5laW4iLCAiZGVubiIsICJlciIsICJwcm9qZWt0IiwgY3VzdG9tX3N0b3B3b3JkcykpICkgIyByZW1vdmUgY3VzdG9tIHN0b3B3b3Jkcwp9CgojJyBHZW5lcmF0ZXMgQmFycGxvdCBvZiBtb3N0IGNvbW1vbiB3b3Jkcy4KIycKIycgQHBhcmFtIGRmIElucHV0ICh0b2tlbml6ZWQpIGRhdGFmcmFtZS4gCiMnIEBwYXJhbSBjdXJyX3EgZGF0YWZyYW1lIHdpdGggY29ycmVzcG9uZGVuY2Ugb2YgY3VycmVudCBxdWVzdGlvbiBudW1iZXJzIGFjcm9zcyB0aGUgc3VydmV5cyBhbmQgbGFiZWxzIGZvciBwcmludGluZyB0aXRsZXMuCiMnIEByZXR1cm4gQmFycGxvdApjb3VudF93b3Jkc191bmdyb3VwZWQgPC0gZnVuY3Rpb24oZGYsIGN1cnJfcSl7CiAgY2xlYW4uZGYgJT4lCiAgICBjb3VudCh0b2tlbiwgc29ydCA9IFRSVUUpICU+JQogICAgZmlsdGVyKG4gPiAxKSAlPiUgCiAgICB0b3BfbihuID0gOSwgd3Q9IG4pICU+JSAKICAgIG11dGF0ZSh0b2tlbiA9IHJlb3JkZXIodG9rZW4sIG4pKSAlPiUKICAgIGdncGxvdChhZXModG9rZW4sIG4pKSArCiAgICB0aGVtZV9saWdodCgpICsKICAgIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICAgIGNvb3JkX2ZsaXAoKSArIGdndGl0bGUodW5pcXVlKGN1cnJfcSRMYWJlbCksIHN1YnRpdGxlID0gIlRvcCAxMCB3b3JkcyIpCn0KCiMnIEdlbmVyYXRlcyBCYXJwbG90IG9mIG1vc3QgY29tbW9uIHdvcmRzIEJZIEdFTkRFUi4KIycKIycgQHBhcmFtIGRmIElucHV0ICh0b2tlbml6ZWQpIGRhdGFmcmFtZS4gCiMnIEBwYXJhbSBjdXJyX3EgZGF0YWZyYW1lIHdpdGggY29ycmVzcG9uZGVuY2Ugb2YgY3VycmVudCBxdWVzdGlvbiBudW1iZXJzIGFjcm9zcyB0aGUgc3VydmV5cyBhbmQgbGFiZWxzIGZvciBwcmludGluZyB0aXRsZXMuCiMnIEByZXR1cm4gQmFycGxvdApjb3VudF93b3Jkc19ncm91cF9nZW5kZXIgPC0gZnVuY3Rpb24oZGYsIGN1cnJfcSl7CiAgZGYgJT4lCiAgZmlsdGVyKEdlc2NobGVjaHQgJWluJSBjKCJtw6RubmxpY2giLCAid2VpYmxpY2giKSkgJT4lIAogIGdyb3VwX2J5KEdlc2NobGVjaHQpICU+JQogIGNvdW50KHRva2VuLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4gPiAxKSAlPiUgCiAgdG9wX24obiA9IDksIHd0PSBuKSAlPiUgCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZSh0b2tlbiA9IHJlb3JkZXIodG9rZW4sIG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHRva2VuLCBuLCBmaWxsID0gR2VzY2hsZWNodCkpICsKICB0aGVtZV9saWdodCgpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfZ3JpZCggfiBHZXNjaGxlY2h0KSArCiAgY29vcmRfZmxpcCgpICsgZ2d0aXRsZSh1bmlxdWUoY3Vycl9xJExhYmVsKSwgc3VidGl0bGUgPSAiVG9wIDEwIHdvcmRzIGJ5IEdlbmRlciIpCn0KCiMnIEdlbmVyYXRlcyBCYXJwbG90IG9mIG1vc3QgY29tbW9uIHdvcmRzIEJZIFlFQVIKIycKIycgQHBhcmFtIGRmIElucHV0ICh0b2tlbml6ZWQpIGRhdGFmcmFtZS4gCiMnIEBwYXJhbSBjdXJyX3EgZGF0YWZyYW1lIHdpdGggY29ycmVzcG9uZGVuY2Ugb2YgY3VycmVudCBxdWVzdGlvbiBudW1iZXJzIGFjcm9zcyB0aGUgc3VydmV5cyBhbmQgbGFiZWxzIGZvciBwcmludGluZyB0aXRsZXMuCiMnIEByZXR1cm4gQmFycGxvdApjb3VudF93b3Jkc19ncm91cF95ZWFyIDwtIGZ1bmN0aW9uKGRmLCBjdXJyX3EpewogIGRmICU+JQogIGdyb3VwX2J5KEphaHIpICU+JQogIGNvdW50KHRva2VuLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4gPiAxKSAlPiUgCiAgdG9wX24obiA9IDksIHd0PSBuKSAlPiUgCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZSh0b2tlbiA9IHJlb3JkZXIodG9rZW4sIG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHRva2VuLCBuLCBmaWxsID0gSmFocikpICsKICB0aGVtZV9saWdodCgpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfZ3JpZCggfiBKYWhyKSArCiAgY29vcmRfZmxpcCgpICsgZ2d0aXRsZSh1bmlxdWUoY3Vycl9xJExhYmVsKSwgc3VidGl0bGUgPSAiVG9wIDEwIHdvcmRzIG92ZXIgeWVhciIpCn0KCiMnIEdlbmVyYXRlcyBOZXR3b3JrIHdvcmRzIHVzZWQgb3ZlciBzb21lIHRocmVzaG9sZCB3aXRoIGZvbGxvd2luZyBzcGVjczoKIycgTm9kZSBzaXplID0gaG93IG11Y2ggdGhlIHdvcmRzIHdhcyB1c2VkLgojJyBFZGdlIHNpemUgPSBob3cgbWFueSB0aW1lcyB0aGVzZSB3b3JkcyBoYXZlIGJlZW4gdXNlZCB0b2dldGhlci4KIycKIycgQHBhcmFtIGRmIElucHV0ICh0b2tlbml6ZWQpIGRhdGFmcmFtZS4gCiMnIEBwYXJhbSB0aHJlc2hvbGQgdGhyZXNob2xkIGZvIGRpc3BsYXkgd29yZHMgd2l0aCBjb3VudCA+IHRocmVzaG9sZAojJyBAcGFyYW0gY3Vycl9xIGRhdGFmcmFtZSB3aXRoIGNvcnJlc3BvbmRlbmNlIG9mIGN1cnJlbnQgcXVlc3Rpb24gbnVtYmVycyBhY3Jvc3MgdGhlIHN1cnZleXMgYW5kIGxhYmVscyBmb3IgcHJpbnRpbmcgdGl0bGVzLgojJyBAcGFyYW0gZWRnZV93IGZhY3RvciB0byBzY2FsZSBlZGdlIHdpZHRoCiMnIEBwYXJhbSB2ZXJ0ZXhfcyBmYWN0b3IgdG8gc2NhbGUgdmVydGV4IHNpemUKIycgQHJldHVybiBOZXR3b3JrCm5ldHdvcmtfZ2VuZXJhdGUgPC0gZnVuY3Rpb24oZGYgPSBjbGVhbi5kZiwgdGhyZXNob2xkLCBjdXJyX3EsIGVkZ2VfdyA9IDMsIHZlcnRleF9zID0gMS8yLCB2ZXJ0ZXhfbGFiZWxfZGlzdCA9IDAuNiwgdmVydGV4X2xhYmVsX2NleCA9IDEuOCl7CiAgIyBXZSBzYXZlIHRoZSBuZXR3b3JrIHBsb3QgYXMgYSBwZGYgZmlsZS4KICAjcGRmKHBhc3RlKCJleHBvcnRlZF9udyIsIHVuaXF1ZShjdXJyX3EkTGFiZWwpLCBwYXN0ZSh1bmlxdWUoZGYkSmFociksIGNvbGxhcHNlID0gIl8iKSwgIi5wZGYiKSkKICAjIENvbXB1dGUgcGFpcndpc2Ugd29yZCBjb3VudC4gCiAgcGFpcl93aXNlX2NvdW50X2RmIDwtIGRmICU+JSAKICBzZWxlY3QodG9rZW4sIHJvd2lkKSAlPiUKICBwYWlyd2lzZV9jb3VudChpdGVtID0gdG9rZW4sIGZlYXR1cmUgPSByb3dpZCkgJT4lIAogIGZpbHRlcihpdGVtMSA8IGl0ZW0yKSAlPiUgYXJyYW5nZShpdGVtMSwgaXRlbTIpICU+JSAKICBhcnJhbmdlKC0gbikKICAjIERlZmluZSBuZXR3b3JrIG9iamVjdC4gCiAgbmV0d29yayA8LSAgcGFpcl93aXNlX2NvdW50X2RmICU+JQogICAgZmlsdGVyKG4gPiB0aHJlc2hvbGQpICU+JSAjIGFwcGx5IHRocmVzaG9sZAogICAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGRpcmVjdGVkID0gRkFMU0UpCiAgCiAgIyBTdG9yZSB0aGUgZGVncmVlLgogIFYobmV0d29yaykkZGVncmVlIDwtIGRlZ3JlZShncmFwaCA9IG5ldHdvcmspCiAgCiAgIyBDb21wdXRlIHRoZSB3ZWlnaHQgc2hhcmVzLgogIEUobmV0d29yaykkd2lkdGggPC0gRShuZXR3b3JrKSRuL21heChFKG5ldHdvcmspJG4pCiAgIAogICMgUGxvdCB0aGUgbmV0d29yay4KICBwbG90KG5ldHdvcmssIAogICAgICAgdmVydGV4LmNvbG9yID0gJ2xpZ2h0Ymx1ZScsCiAgICAgICAjIFNjYWxlIG5vZGUgc2l6ZSBieSBkZWdyZWUuCiAgICAgICB2ZXJ0ZXguc2l6ZSA9IHZlcnRleF9zKlYobmV0d29yaykkZGVncmVlLAogICAgICAgdmVydGV4LmxhYmVsLmNvbG9yID0gJ2JsYWNrJywgCiAgICAgICB2ZXJ0ZXgubGFiZWwuY2V4ID0gdmVydGV4X2xhYmVsX2NleCwgCiAgICAgICB2ZXJ0ZXgubGFiZWwuZGlzdCA9IHZlcnRleF9sYWJlbF9kaXN0LAogICAgICAgZWRnZS5jb2xvciA9ICdncmF5JywgCiAgICAgICAjIFNldCBlZGdlIHdpZHRoIHByb3BvcnRpb25hbCB0byB0aGUgd2VpZ2h0IHJlbGF0aXZlIHZhbHVlLgogICAgICAgZWRnZS53aWR0aCA9IGVkZ2VfdypFKG5ldHdvcmspJHdpZHRoLAogICAgICAgbWFpbiA9ICBwYXN0ZSh1bmlxdWUoY3Vycl9xJExhYmVsKSwgImZvciBzdXZleXM6IiwgcGFzdGUodW5pcXVlKGRmJEphaHIpLCBjb2xsYXBzZSA9ICIsIikpLCAKICAgICAgIHN1YiA9IGdsdWUoJ1dlaWdodCBUaHJlc2hvbGQ6IHt0aHJlc2hvbGR9JyksIAogICAgICAgYWxwaGEgPSA1MCkKICAjZGV2Lm9mZigpCn0KYGBgCgojIFNPRyBJbXByb3ZlbWVudHMKCmBgYHtyfQpjdXJyX3FfbnVtIDwtIDEKY3Vycl9xIDwtIHF1ZXN0aW9uc19mb3JfYW5hbHlzaXMgJT4lIGZpbHRlcihJRCA9PSBjdXJyX3FfbnVtKQpjbGVhbi5kZiA8LSB0b2tlbml6ZV9hbmRfY2xlYW4oZGF0YV9jYXRlZ29yaXplZCwgY3Vycl9xLCBjdXN0b21fc3RvcHdvcmRzID0gYygiZ2lidCIsICJpbnN0aXR1dGlvbiIsICJwcm9qZWt0IiwgInN0aXBlbmRpZW5wcm9ncmFtbSIsICJwcm9ncmFtbSIsICJzdGlwZW5kaWF0ZW4iKSkKYGBgCgpgYGB7cn0KY291bnRfd29yZHNfdW5ncm91cGVkKGNsZWFuLmRmLCBjdXJyX3EpCmBgYAoKYGBge3J9CmNvdW50X3dvcmRzX2dyb3VwX2dlbmRlcihjbGVhbi5kZiwgY3Vycl9xKQpgYGAKCmBgYHtyfQpjb3VudF93b3Jkc19ncm91cF95ZWFyKGNsZWFuLmRmLCBjdXJyX3EpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9MjB9CiMgU2V0IGEgdGhyZXNob2xkIGZvciB2aXN1YWxpemF0aW9uLgp0aHJlc2hvbGQgPC0gMQoKbmV0d29ya19nZW5lcmF0ZShkZiA9IGNsZWFuLmRmLCB0aHJlc2hvbGQsIGN1cnJfcSwgZWRnZV93ID0gMywgdmVydGV4X3MgPSAxLzIsIHZlcnRleF9sYWJlbF9kaXN0ID0gMC42LCB2ZXJ0ZXhfbGFiZWxfY2V4ID0gMS44KQpgYGAKCmBgYHtyfQojIFNldCBhIHRocmVzaG9sZCBmb3IgdmlzdWFsaXphdGlvbi4KI3RocmVzaG9sZCA8LSAxCgojbmV0d29ya19nZW5lcmF0ZShkZiA9IGZpbHRlcihjbGVhbi5kZiwgSmFociA9PSAyMDE0KSwgdGhyZXNob2xkLCBjdXJyX3EpCmBgYAoKYGBge3J9CiMgU2V0IGEgdGhyZXNob2xkIGZvciB2aXN1YWxpemF0aW9uLgojdGhyZXNob2xkIDwtIDEKCiNuZXR3b3JrX2dlbmVyYXRlKGRmID0gZmlsdGVyKGNsZWFuLmRmLCBKYWhyID09IDIwMTUpLCB0aHJlc2hvbGQsIGN1cnJfcSkKYGBgCgpgYGB7cn0KIyBTZXQgYSB0aHJlc2hvbGQgZm9yIHZpc3VhbGl6YXRpb24uCiN0aHJlc2hvbGQgPC0gMAoKI25ldHdvcmtfZ2VuZXJhdGUoZGYgPSBmaWx0ZXIoY2xlYW4uZGYsIEphaHIgPT0gMjAxNyksIHRocmVzaG9sZCwgY3Vycl9xKQpgYGAKCgojIERpZmZpY3VsdGllcyBpbiB0aGUgcGFzdCBhY2FkZW1pYyB5ZWFyCgpgYGB7cn0KY3Vycl9xX251bSA8LSAyCmN1cnJfcSA8LSBxdWVzdGlvbnNfZm9yX2FuYWx5c2lzICU+JSBmaWx0ZXIoSUQgPT0gY3Vycl9xX251bSkKY2xlYW4uZGYgPC0gdG9rZW5pemVfYW5kX2NsZWFuKGRhdGFfY2F0ZWdvcml6ZWQsIGN1cnJfcSwgY3VzdG9tX3N0b3B3b3JkcyA9IGMoInNjaHdpZXJpZ2tlaXRlbiIpKQpgYGAKCmBgYHtyfQpjb3VudF93b3Jkc191bmdyb3VwZWQoY2xlYW4uZGYsIGN1cnJfcSkKYGBgCgpgYGB7cn0KY291bnRfd29yZHNfZ3JvdXBfZ2VuZGVyKGNsZWFuLmRmLCBjdXJyX3EpCmBgYAoKYGBge3J9CmNvdW50X3dvcmRzX2dyb3VwX3llYXIoY2xlYW4uZGYsIGN1cnJfcSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTIwfQojIFNldCBhIHRocmVzaG9sZCBmb3IgdmlzdWFsaXphdGlvbi4KdGhyZXNob2xkIDwtIDEKCm5ldHdvcmtfZ2VuZXJhdGUoZGYgPSBjbGVhbi5kZiwgdGhyZXNob2xkLCBjdXJyX3EsIGVkZ2VfdyA9IDMsIHZlcnRleF9zID0gMS8yLCB2ZXJ0ZXhfbGFiZWxfZGlzdCA9IDAuNiwgdmVydGV4X2xhYmVsX2NleCA9IDEuOCkKYGBgCgpgYGB7cn0KIyBTZXQgYSB0aHJlc2hvbGQgZm9yIHZpc3VhbGl6YXRpb24uCiN0aHJlc2hvbGQgPC0gMQoKI25ldHdvcmtfZ2VuZXJhdGUoZGYgPSBmaWx0ZXIoY2xlYW4uZGYsIEphaHIgPT0gMjAxNCksIHRocmVzaG9sZCwgY3Vycl9xKQpgYGAKCmBgYHtyfQojIFNldCBhIHRocmVzaG9sZCBmb3IgdmlzdWFsaXphdGlvbi4KI3RocmVzaG9sZCA8LSAxCgojbmV0d29ya19nZW5lcmF0ZShkZiA9IGZpbHRlcihjbGVhbi5kZiwgSmFociA9PSAyMDE1KSwgdGhyZXNob2xkLCBjdXJyX3EpCmBgYAoKYGBge3J9CiMgU2V0IGEgdGhyZXNob2xkIGZvciB2aXN1YWxpemF0aW9uLgojdGhyZXNob2xkIDwtIDAKCiNuZXR3b3JrX2dlbmVyYXRlKGRmID0gZmlsdGVyKGNsZWFuLmRmLCBKYWhyID09IDIwMTcpLCB0aHJlc2hvbGQsIGN1cnJfcSkKYGBgCgoKCgojIFBvc3NpYmxlIHJpc2tzIG9mIHByb2plY3QgaW1wbGVtZW50YXRpb24KCmBgYHtyfQpjdXJyX3FfbnVtIDwtIDMKY3Vycl9xIDwtIHF1ZXN0aW9uc19mb3JfYW5hbHlzaXMgJT4lIGZpbHRlcihJRCA9PSBjdXJyX3FfbnVtKQpjbGVhbi5kZiA8LSB0b2tlbml6ZV9hbmRfY2xlYW4oZGF0YV9jYXRlZ29yaXplZCwgY3Vycl9xKQpgYGAKCmBgYHtyfQpjb3VudF93b3Jkc191bmdyb3VwZWQoY2xlYW4uZGYsIGN1cnJfcSkKYGBgCgpgYGB7cn0KY291bnRfd29yZHNfZ3JvdXBfZ2VuZGVyKGNsZWFuLmRmLCBjdXJyX3EpCmBgYAoKYGBge3J9CmNvdW50X3dvcmRzX2dyb3VwX3llYXIoY2xlYW4uZGYsIGN1cnJfcSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTIwfQojIFNldCBhIHRocmVzaG9sZCBmb3IgdmlzdWFsaXphdGlvbi4KdGhyZXNob2xkIDwtIDEKCm5ldHdvcmtfZ2VuZXJhdGUoZGYgPSBjbGVhbi5kZiwgdGhyZXNob2xkLCBjdXJyX3EpCmBgYAoKYGBge3J9CiMgU2V0IGEgdGhyZXNob2xkIGZvciB2aXN1YWxpemF0aW9uLgojdGhyZXNob2xkIDwtIDEKCiNuZXR3b3JrX2dlbmVyYXRlKGRmID0gZmlsdGVyKGNsZWFuLmRmLCBKYWhyID09IDIwMTQpLCB0aHJlc2hvbGQsIGN1cnJfcSkKYGBgCgpgYGB7cn0KIyBTZXQgYSB0aHJlc2hvbGQgZm9yIHZpc3VhbGl6YXRpb24uCiN0aHJlc2hvbGQgPC0gMQoKI25ldHdvcmtfZ2VuZXJhdGUoZGYgPSBmaWx0ZXIoY2xlYW4uZGYsIEphaHIgPT0gMjAxNSksIHRocmVzaG9sZCwgY3Vycl9xKQpgYGAKCmBgYHtyfQojIFNldCBhIHRocmVzaG9sZCBmb3IgdmlzdWFsaXphdGlvbi4KI3RocmVzaG9sZCA8LSAwCgojbmV0d29ya19nZW5lcmF0ZShkZiA9IGZpbHRlcihjbGVhbi5kZiwgSmFociA9PSAyMDE3KSwgdGhyZXNob2xkLCBjdXJyX3EpCmBgYAoKCgoKIyBDaGFuZ2VzIGluIExpZmUgU2l0dWF0aW9uCgpgYGB7cn0KY3Vycl9xX251bSA8LSA1CmN1cnJfcSA8LSBxdWVzdGlvbnNfZm9yX2FuYWx5c2lzICU+JSBmaWx0ZXIoSUQgPT0gY3Vycl9xX251bSkKY2xlYW4uZGYgPC0gdG9rZW5pemVfYW5kX2NsZWFuKGRhdGFfY2F0ZWdvcml6ZWQsIGN1cnJfcSwgY3VzdG9tX3N0b3B3b3JkcyA9IGMoInNpdHVhdGlvbiIsICJ2ZXLDpG5kZXJ0IiwgInZlcsOkbmRlcnVuZyIsICJqYWhyIiwgImdlw6RuZGVydCIsICJnaWJ0IikpCmBgYAoKYGBge3J9CmNvdW50X3dvcmRzX3VuZ3JvdXBlZChjbGVhbi5kZiwgY3Vycl9xKQpgYGAKCmBgYHtyfQpjb3VudF93b3Jkc19ncm91cF9nZW5kZXIoY2xlYW4uZGYsIGN1cnJfcSkKYGBgCgpgYGB7cn0KY291bnRfd29yZHNfZ3JvdXBfeWVhcihjbGVhbi5kZiwgY3Vycl9xKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MjB9CiMgU2V0IGEgdGhyZXNob2xkIGZvciB2aXN1YWxpemF0aW9uLgp0aHJlc2hvbGQgPC0gMQoKbmV0d29ya19nZW5lcmF0ZShkZiA9IGNsZWFuLmRmLCB0aHJlc2hvbGQsIGN1cnJfcSwgdmVydGV4X3MgPSAxKQpgYGAKCmBgYHtyfQojIFNldCBhIHRocmVzaG9sZCBmb3IgdmlzdWFsaXphdGlvbi4KI3RocmVzaG9sZCA8LSAxCgojbmV0d29ya19nZW5lcmF0ZShkZiA9IGZpbHRlcihjbGVhbi5kZiwgSmFociA9PSAyMDE0KSwgdGhyZXNob2xkLCBjdXJyX3EpCmBgYAoKYGBge3J9CiMgU2V0IGEgdGhyZXNob2xkIGZvciB2aXN1YWxpemF0aW9uLgojdGhyZXNob2xkIDwtIDEKCiNuZXR3b3JrX2dlbmVyYXRlKGRmID0gZmlsdGVyKGNsZWFuLmRmLCBKYWhyID09IDIwMTUpLCB0aHJlc2hvbGQsIGN1cnJfcSkKYGBgCgpgYGB7cn0KIyBTZXQgYSB0aHJlc2hvbGQgZm9yIHZpc3VhbGl6YXRpb24uCiN0aHJlc2hvbGQgPC0gMAoKI25ldHdvcmtfZ2VuZXJhdGUoZGYgPSBmaWx0ZXIoY2xlYW4uZGYsIEphaHIgPT0gMjAxNyksIHRocmVzaG9sZCwgY3Vycl9xKQpgYGAKCgoKIyBXaXNoZXMgZm9yIG5leHQgeWVhcgoKYGBge3J9CmN1cnJfcV9udW0gPC0gNgpjdXJyX3EgPC0gcXVlc3Rpb25zX2Zvcl9hbmFseXNpcyAlPiUgZmlsdGVyKElEID09IGN1cnJfcV9udW0pCmNsZWFuLmRmIDwtIHRva2VuaXplX2FuZF9jbGVhbihkYXRhX2NhdGVnb3JpemVkLCBjdXJyX3EsIGN1c3RvbV9zdG9wd29yZHMgPSBjKCJqYWhyIiwgIm3DtmNodGUiLCAibsOkY2h0ZXMiLCAibsOkY2hzdGVzIiwgInfDvG5zY2hlIiwgInd1bnNjaCIsICJob2ZmdCIpKQpgYGAKCmBgYHtyfQpjb3VudF93b3Jkc191bmdyb3VwZWQoY2xlYW4uZGYsIGN1cnJfcSkKYGBgCgpgYGB7cn0KY291bnRfd29yZHNfZ3JvdXBfZ2VuZGVyKGNsZWFuLmRmLCBjdXJyX3EpCmBgYAoKYGBge3J9CmNvdW50X3dvcmRzX2dyb3VwX3llYXIoY2xlYW4uZGYsIGN1cnJfcSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTIwfQojIFNldCBhIHRocmVzaG9sZCBmb3IgdmlzdWFsaXphdGlvbi4KdGhyZXNob2xkIDwtIDEKCm5ldHdvcmtfZ2VuZXJhdGUoZGYgPSBjbGVhbi5kZiwgdGhyZXNob2xkLCBjdXJyX3EpCmBgYAoKYGBge3J9CiMgU2V0IGEgdGhyZXNob2xkIGZvciB2aXN1YWxpemF0aW9uLgojdGhyZXNob2xkIDwtIDEKCiNuZXR3b3JrX2dlbmVyYXRlKGRmID0gZmlsdGVyKGNsZWFuLmRmLCBKYWhyID09IDIwMTQpLCB0aHJlc2hvbGQsIGN1cnJfcSkKYGBgCgpgYGB7cn0KIyBTZXQgYSB0aHJlc2hvbGQgZm9yIHZpc3VhbGl6YXRpb24uCiN0aHJlc2hvbGQgPC0gMQoKI25ldHdvcmtfZ2VuZXJhdGUoZGYgPSBmaWx0ZXIoY2xlYW4uZGYsIEphaHIgPT0gMjAxNSksIHRocmVzaG9sZCwgY3Vycl9xKQpgYGAKCmBgYHtyfQojIFNldCBhIHRocmVzaG9sZCBmb3IgdmlzdWFsaXphdGlvbi4KI3RocmVzaG9sZCA8LSAwCgojbmV0d29ya19nZW5lcmF0ZShkZiA9IGZpbHRlcihjbGVhbi5kZiwgSmFociA9PSAyMDE3KSwgdGhyZXNob2xkLCBjdXJyX3EpCmBgYAoK